package ar.com.agea.processor.alert.controller

import grails.converters.JSON
import groovyx.net.http.ContentType
import groovyx.net.http.Method

import org.codehaus.groovy.grails.web.converters.exceptions.ConverterException
import org.springframework.context.MessageSource

import ar.com.agea.domain.alerts.PubAlertAssociation
import ar.com.agea.exception.ApplicationException
import ar.com.agea.processes.alert.Constants
import ar.com.agea.util.ExternalApiHandler
import ar.com.agea.util.StringUtils


class AlertNotifierController {

	def publicationOrderService
	def alertFeatureService
	def alertService
	def publishableItemService
	MessageSource messageSource
	def pubAlertAssociationService

	private Integer maxPublications = null
	private ExternalApiHandler apiHandler = new ExternalApiHandler()

	def notifyAlert(intervalCode) {
		// seteo la cantidad maxima de publicaciones
		maxPublications = grailsApplication.config.max.publications
		def actived = true
		def maxPubsPerMail = false
		def publicationsMatched = [:]
		def pubList = [:]
		def results = alertService.findAlertsByStateAndIntervalCode(actived, intervalCode).each { alert ->

			publicationsMatched.clear()

			log.info "Searching publications for alert with id: $alert.id, interval: $alert.alertInterval.time days"
			def alertIntervalDays = alert.alertInterval.time

			//Calculo la fecha con la cual se va a comparar.
			def currentDate = new Date()
			Calendar cal = new GregorianCalendar();
			cal.setTimeInMillis(currentDate.getTime());
			cal.add(Calendar.DATE, -alertIntervalDays);
			def dateToCompare = new Date(cal.getTimeInMillis());

			//Request api deautos
			def results = requestToApi(alert.url)
			def jsonResp = parseResponse(results)

			//			def morePublications = false
			def countPubMatched = 0
			jsonResp.SearchResult.Results.each { result ->

				def publicationDataID = result.PublicationData.PUBLICATION_DATA_ID.AttributeValue
				def publicationOrder = publicationOrderService.get(publicationDataID)

				// la publicacion ha sido modificada
				if (dateToCompare.before(publicationOrder.lastUpdated) && publicationOrder.publicationState.equals(Constants.STATE_ACTIVE)) {

					def publishableItemDataId = result.PublishableItemData.PUBLISHABLE_ITEM_DATA_ID.AttributeValue as Long

					def picture = "${grailsApplication.config.url.api.imagenes}${publishableItemDataId}/520X390"
					def brand = result.PublishableItemData.PUBLISHABLE_ITEM_DATA_VEHICLE_MAKE_DESCRIPTION.AttributeValue
					def model = result.PublishableItemData.PUBLISHABLE_ITEM_DATA_VEHICLE_MODEL_DESCRIPTION.AttributeValue
					def carVersion = result.PublishableItemData.PUBLISHABLE_ITEM_DATA_VEHICLE_VERSION_DESCRIPTION.AttributeValue
					def year = result.PublishableItemData.PUBLISHABLE_ITEM_DATA_YEAR.AttributeValue
					def segment = result.PublishableItemData.PUBLISHABLE_ITEM_DATA_VEHICLE_SEGMENT_DESCRIPTION.AttributeValue
					def fuelType = result.PublishableItemData.PUBLISHABLE_ITEM_DATA_FUEL_TYPE_DESCRIPTION.AttributeValue

					def province = result.PublishableItemData.PUBLISHABLE_ITEM_DATA_GEOLOCATION.AttributeValue.Province.Name
					def locality = result.PublishableItemData.PUBLISHABLE_ITEM_DATA_GEOLOCATION.AttributeValue.Localidad.Name
					def partido = result.PublishableItemData.PUBLISHABLE_ITEM_DATA_GEOLOCATION.AttributeValue.Partido.Name

					def symbolPayment = result.PublishableItemData.PUBLISHABLE_ITEM_DATA_PRICE_CURRENCY_SYMBOL.AttributeValue
					def methodPayment = result.PublishableItemData.PUBLISHABLE_ITEM_DATA_ACCEPTED_PAYMENT_METHOD_DESCRIPTION.AttributeValue


					def urlPublication = result.PublicationData.PUBLICATION_DATA_PUBLICATION_URL.AttributeValue

					def price =  result.PublishableItemData.PUBLISHABLE_ITEM_DATA_PRICE.AttributeValue
					if ((result.PublishableItemData.PUBLISHABLE_ITEM_DATA_CHECK_PRICE.AttributeValue).equals("True")) {
						price = "Consultar"
					}

					//cuenta las publicaciones que se van a enviar
					countPubMatched += 1

					def brandModelVersion = "$brand $model $carVersion"
					def location = "$province - $locality - $partido"
					def aPublication = ["brandModelVersion$countPubMatched" : brandModelVersion, "brand$countPubMatched": brand,
						"model$countPubMatched": model, "year$countPubMatched": year, "segment$countPubMatched": segment,
						"fuelType$countPubMatched": fuelType,"location$countPubMatched": location, "symbolPayment$countPubMatched": symbolPayment,
						"paymentmethod$countPubMatched": methodPayment, "price$countPubMatched": price, "urlPub$countPubMatched": urlPublication,
						"picprincipal$countPubMatched": picture
					]

					//Si no pasa el limite se agrega para ser enviada
					if (!maxPubsPerMail) publicationsMatched << aPublication

					def publishableItemInstance = publishableItemService.show(publishableItemDataId)
					def userInstance = publishableItemInstance.user
					def isAgency = userInstance.userType.equals(Constants.AGENCY_USER_ID)

					// Si es agencia se le envia un mail
					if (isAgency) {
						notifyToAgency(brandModelVersion, alert, userInstance, publishableItemDataId)
					}


				}

				// Si ya se encontraron las publicaciones maximas, se sigue iterando para avisar a las agencias pero no se agregaran pubs para enviarlas
				// al usuario como alerta.
				if (countPubMatched == maxPublications) {
					maxPubsPerMail = true
				}

			}

			//concatena el nombre por las facetas buscadas
			def featuresBuilder = concatFeaturesNames(alert)

			// Si encontro algun publicacion para la alerta, se prepara los datos y se envia el mail
			if (countPubMatched > 0) {
				def urlSearch = grailsApplication.config.url.site + "autos/$alert.url"
				def subject = messageSource.getMessage("subject.mail.alert.user", [featuresBuilder] as String[], Locale.default)
				pubList = ["subject": subject, "to": alert.user.email, "userMail": alert.user.email, "alertName": alert.alertName, "features": featuresBuilder, "urlSearch": urlSearch] << publicationsMatched
				addCommentRows(countPubMatched, pubList)
				log.info "Sending mail for alertId: $alert.id with $countPubMatched publications"
				sendMail(pubList, Constants.MAIL_CONF_USER)

			}
			
			else {
				log.info ("No publications for the alert with id: ${alert.id}")
			}
		}
	}



	def notifyToAgency(brandModelVersion, alert, agencyUser, pubsId) {
		def geoUser = agencyUser.geoLocationItem.geoLocationParentItem.id
		def geoAlertUser = alert.user.geoLocationItem.geoLocationParentItem.id
		def pubsAlertAssociation = pubAlertAssociationService.findByAlertIdAndPublishableId(alert.id, pubsId)

		// Si el usuario y la publicacion coinciden en el partido, y no se la ha mandado un mail a la agencia aun...
		if ((geoUser == geoAlertUser) & (pubsAlertAssociation == null)) {

			def args = [agencyUser.brandName, (alert.publishableItemType.id == 3) ? Constants.SAVING_PLANS : Constants.VEHICLES]
			def subject = messageSource.getMessage("subject.mail.alert.agency", args as String[], Locale.default)

			def contentMail = [
				subject : subject,
				brandModelVersion : brandModelVersion,
				picPrincipal : "${grailsApplication.config.url.api.imagenes}${pubsId}/520X390",
				usermail: alert.user.email,
				to: agencyUser.email,
				agencyName: agencyUser.brandName,
				publishableItemTypeDescription: (alert.publishableItemType.id == 3) ? Constants.SAVING_PLAN : Constants.VEHICLE
			]

			//Se le envia unica vez un mail  a la agencia con los datos del usuario alerta.
			sendMail(contentMail, Constants.MAIL_CONF_AGENCY)
			// Registro la asociacion entre la alerta y la publicacion
			pubAlertAssociationService.save(new PubAlertAssociation(alertId: alert.id, publishableId: pubsId))

		}

	}



	def sendMail(contentMail, mailConfName) {

		def urlMailService = grailsApplication.config.url.api.mailService
		// se le agregan los datos necesarios
		contentMail << ["AddresseeEMail": contentMail.to, "Subject": contentMail.subject]
		def jsonPubs = (contentMail as JSON).toString()
		def jsonDetails = '''{"mailConfigurationName":''' + "${mailConfName}," + '''"params":''' + "$jsonPubs" + "}"
		jsonDetails = URLEncoder.encode(jsonDetails, "UTF-8");
		def statusMail = apiHandler.doRequest(urlMailService, jsonDetails, Method.GET, ContentType.URLENC) { resp, json -> json }
		log.info "Mail status: $statusMail"

	}

	def concatFeaturesNames(alert) {
		def featuresBuilder = ""
		alertFeatureService.findAllByAlert(alert).each{ alertFeature ->
			featuresBuilder += alertFeature.value + " "
		}
		return featuresBuilder
	}


	def parseResponse(results) {
		def replacementCharsMap = ["\\\\": "", "\"rn": "", '''"{"''': "{\"", '''}","''': '''},"''', '''}"''': '''}''']
		def replacementRegexMap = [":\"\\[" : ":[",  "\\]\"}": "]}", "\\\\\"" : "\""]
		def resultParsed = StringUtils.multipleReplace(StringUtils.multipleReplaceAll(results.toString(), replacementRegexMap), replacementCharsMap)
		try {
			JSON.parse(resultParsed)
		}
		catch(ConverterException error) {
			throw new ApplicationException(error.message)
		}
	}

	def getPicture(publicationId) {
		def ulrApiImagenes = grailsApplication.config.url.api.imagenes
		def urlSearch = publicationId + "/520X390"
		apiHandler.doRequest(ulrApiImagenes, urlSearch, Method.GET, ContentType.JSON) { resp, json -> json }
	}

	def addCommentRows(countPubMatched, pubList) {

		//comment rows
		if (countPubMatched < 4) {
			pubList << ["initCommentRow2": Constants.HTML_INIT_COMMENT, "endCommentRow2": Constants.HTML_END_COMMENT, "initCommentRow3": Constants.HTML_INIT_COMMENT, "endCommentRow3": Constants.HTML_END_COMMENT]
			//comment TD
			if (countPubMatched == 1) {
				pubList << ["initCommentTd2Row1": Constants.HTML_INIT_COMMENT, "endCommentTd2Row1": Constants.HTML_END_COMMENT,
					"bordernonetd2row1": Constants.HTML_BORDER_NONE, "initCommentTd3Row1": Constants.HTML_INIT_COMMENT, "endCommentTd3Row1": Constants.HTML_END_COMMENT, "bordernonetd3row1": Constants.HTML_BORDER_NONE]
			}

			else if (countPubMatched == 2) {
				pubList << [ "initCommentTd3Row1": Constants.HTML_INIT_COMMENT, "endCommentTd3Row1": Constants.HTML_END_COMMENT, "bordernonetd3row1": Constants.HTML_BORDER_NONE]
			}
		}

		else if (countPubMatched > 3 && countPubMatched < 7) {
			pubList << ["initCommentRow3": Constants.HTML_INIT_COMMENT, "endCommentRow3": Constants.HTML_END_COMMENT]
			//comment TD
			if (countPubMatched == 4) {
				pubList << ["initCommentTd2Row2": Constants.HTML_INIT_COMMENT, "endCommentTd2Row2": Constants.HTML_END_COMMENT,
					"bordernonetd2row2": Constants.HTML_BORDER_NONE, "initCommentTd3Row2": Constants.HTML_INIT_COMMENT, "endCommentTd3Row2": Constants.HTML_END_COMMENT, "bordernonetd3row2": Constants.HTML_BORDER_NONE]
			}

			else if (countPubMatched == 5) {
				pubList << [ "initCommentTd3Row2": Constants.HTML_INIT_COMMENT, "endCommentTd3Row2": Constants.HTML_END_COMMENT, "bordernonetd3row2": Constants.HTML_BORDER_NONE]
			}
		}

		else {
			//comment TD
			if (countPubMatched == 7) {
				pubList << ["initCommentTd2Row3": Constants.HTML_INIT_COMMENT, "endCommentTd2Row3": Constants.HTML_END_COMMENT, "bordernonetd2row3": Constants.HTML_BORDER_NONE, "initCommentTd3Row3": Constants.HTML_INIT_COMMENT, "endCommentTd3Row3": Constants.HTML_END_COMMENT, "bordernonetd3row3": Constants.HTML_BORDER_NONE]
			}
			else if (countPubMatched == 8) {
				pubList << [ "initCommentTd3Row3": Constants.HTML_INIT_COMMENT, "endCommentTd3Row3": Constants.HTML_END_COMMENT, "bordernonetd3row3": Constants.HTML_BORDER_NONE]
			}
		}



	}


	def requestToApi(urlSearch) {
		def username = grailsApplication.config.api.autos.username
		def pass = grailsApplication.config.api.autos.pass
		def headersMap = [ "username": username, "password": pass]
		def urlApiAutos = grailsApplication.config.url.api.autos
		apiHandler.doRequest(urlApiAutos, urlSearch, Method.GET, headersMap, ContentType.JSON) { resp, json -> json }
	}

}


